Skip to content

15 DOM 获取元素

Xmind

DOM get element xmind

了解 DOM 的结构并掌握其基本的操作,体验 DOM 的在开发中的作用

  • 知道 ECMAScript 与 JavaScript 的关系
  • 了解 DOM 的相关概念及 DOM 的本质是一个对象
  • 掌握查找节点的基本方法
  • 掌握节点属性和文本的操作
  • 能够使用间歇函数创建定时任务

Web API 基本认知

  1. 知道 ECMAScript 与 JavaScript 的关系,Web APIs 是浏览器扩展的功能。
  2. Web API 阶段我们学习那两部分?
    • DOM
    • BOM
  3. DOM 是什么?有什么作用?
    • DOM 是 文档对象模型
    • 操作网页内容,可以开发网页内容特效和实现用户交互
  4. DOM 树是什么?
    • 将 HTML 文档以树状结构直观的表现出来,我们称之为文档树DOM 树
    • 作用:文档树直观的体现了标签与标签之间的关系
  5. DOM 对象怎么创建的?
    • 浏览器根据 html 标签生成的 JS 对象(DOM 对象)
    • DOM 的核心就是把内容当 对象 来处理
  6. document 是什么?
    • 是 DOM 里提供的一个 对象
    • 网页所有内容都在 document 里面

严格意义上讲,我们在 JavaScript 阶段学习的知识绝大部分属于 ECMAScript 的知识体系,ECMAScript 简称 ES 它提供了一套语言标准规范,如变量、数据类型、表达式、语句、函数等语法规则都是由 ECMAScript 规定的。浏览器将 ECMAScript 大部分的规范加以实现,并且在此基础上又扩展一些实用的功能,这些被扩展出来的内容我们称为 Web APIs。

ECMAScript 运行在浏览器中然后再结合 Web APIs 才是真正的 JavaScript,Web APIs 的核心是 DOM 和 BOM。

ECMAScript

  • 定义了语法,这是写 JavaScript 和 Node.js 都必须遵守的。
  • 语法包含变量定义,循环、判断、函数、原型和原型链,作用域和闭包、异步
  • 不能操作 DOM,不能监听 click 事件,不能发送 ajax 请求
  • 不能处理 HTTP 请求,不能操作文件
  • ECMAScript 仅仅是一个语法规范

JavaScript

  • 使用 JavaScript 语法规范,外加 Web API(使用 W3C 标准),缺一不可
  • Web API 包含 DOM 操作,BOM 操作,事件绑定,Ajax 等
  • 两者结合(es 语法规范 +Web API),即可完成浏览器端(可理解为前端)的任何操作
  • 基于 js 引擎,不同的浏览器有不同的 js 引擎

Node.js

  • 使用 ECMAScript 语法规范,外加 Node.js API,缺一不可
  • Node.js API 包含 OS(操作系统模块)、file(文件系统模块)、net(网络系统模块)、database(数据库模块)
  • 两者结合(es 语法规范 +Node.js API),即可完成服务端(可理解为后端)的任何操作
  • 基于 Google 的 V8 引擎,执行速度快,性能好

JavaScript 和 Node.js

  • Node.js = ECMAScript + Node.js API
  • JavaScript = ECMAScript + Web API = ECMAScript + DOM + BOM

Node.js 就是运行在服务端(后端)的 JavaScript。Node.js 是一个异步的(非阻塞 I/O)事件驱动的 JavaScript 的运行环境。

JavaScript 和 Node.js

  • 运行环境
    • JavaScript 运行在浏览器端(前端)
    • Node.js 运行在服务端(后端)
  • 全局对象
    • JavaScript 全局对象为 window
    • Node.js 全局对象为 global
  • 全局属性
    • JavaScript 中定义的全局变量可以通过 window 访问
    • Node.js 中定义的全局属性不能通过 global 访问(因为在 Node.js 中定义的变量是模块中的对象,而并不是 global 全局变量)
  • 模块化
    • JavaScript 的模块化经历如下过程:CommonJS 规范—>AMD 规范—>CMD 规范 —>ES6 模块化
    • Node.js 采用的是 CommonJS 规范,使用 require module.exports 的写法,不支持 ES6 中 import export 的写法

Web APIs

JS 的组成

web-apis-intro

JS 基础阶段以及 Web APIs 阶段

JS 基础学习 ECMAScript 基础语法为后面作铺垫,Web APIs 是 JS 的应用,大量使用 JS 基础语法做交互效果

  • JS 基础阶段

    • 我们学习的是 ECMAScript 标准规定的基本语法
    • 要求同学们掌握 JS 基础语法
    • 只学习基本语法,做不了常用的网页交互效果
    • 目的是为了 JS 后面的课程打基础、做铺垫
  • Web APIs 阶段

    • Web APIs 是 W3C 组织的标准
    • Web APIs 我们主要学习 DOM 和 BOM
    • Web APIs 是我们 JS 所独有的部分
    • 我们主要学习页面交互功能
    • 需要使用 JS 基础的课程内容做基础

API 和 Web API

  • API
    • APl(Application Programming Interface,应用程序编程接口)是一些预先定义的函数,目的是提供应用程序
    • 与开发人员基于某软件或硬件得以访问一组例程的能力,而又无需访问源码,或理解内部工作机制的细节。
    • 简单理解:API 是给程序员提供的一种工具,以便能更轻松的实现想要完成的功能。
    • 比如手机充电的接口:
      • 我们要实现充电这个功能:
      • 我们不关心手机内部变压器,内部怎么存储电等
      • 我们不关心这个充电线怎么制作的
      • 我们只知道,我们拿着充电线插进充电接口就可以充电
      • 这个充电接口就是一个 API
  • Web API
    • Web API 是浏览器提供的一套操作浏览器功能和页面元素的 API(BOM 和 DOM)。
    • 现阶段我们主要针对于浏览器讲解常用的 APl,主要针对浏览器做交互效果。
    • 比如我们想要浏览器弹出一个警示框,直接使用 alert('弹出')
    • MDN 详细 API: Web API 接口参考 | MDN (mozilla.org)
    • 因为 Web API 很多,所以我们将这个阶段称为 Web APls

web 浏览器的重要部分

web 浏览器的软件中有很多活动的程序片段,而许多片段 web 开发人员无法使用 JavaScript 来控制或操作,因此 Web 浏览器是一个很复杂的软件组合。你可能认为这样的限制是不是好事,但是浏览器被锁定是有充分理由的,主要集中在安全方面。如果一个网站可以访问您存储的密码或其他敏感信息,犹如你一样登录到网站,试想会发生什么?

尽管有局限性,Web API 仍然允许我们访问许多的功能,使我们用 web 页做很多事情。有几个在代码中经常引用的非常明显的部位 - 下面的图表表示了直接出现在 web 页面视图中的浏览器的主要部分:

img

  • window 是载入浏览器的标签,在 JavaScript 中用 Window 对象来表示,使用这个对象的可用方法,你可以返回窗口的大小(参见 Window.innerWidth 和 Window.innerHeight),操作载入窗口的文档,存储客户端上文档的特殊数据(例如使用本地数据库或其他存储设备),为当前窗口绑定 event handler,等等。
  • navigator 表示浏览器存在于 web 上的状态和标识(即用户代理)。在 JavaScript 中,用 Navigator 来表示。你可以用这个对象获取一些信息,比如来自用户摄像头的地理信息、用户偏爱的语言、多媒体流等等。
  • document(在浏览器中用 DOM 表示)是载入窗口的实际页面,在 JavaScript 中用 Document 对象表示,你可以用这个对象来返回和操作文档中 HTML 和 CSS 上的信息。例如获取 DOM 中一个元素的引用,修改其文本内容,并应用新的样式,创建新的元素并添加为当前元素的子元素,甚至把他们一起删除。

文档对象模型 DOM

文档对象模型 (DOM) 将 web 页面与到脚本或编程语言连接起来。通常是指 JavaScript,但将 HTML、SVG 或 XML 文档建模为对象并不是 JavaScript 语言的一部分。DOM 模型用一个逻辑树来表示一个文档,树的每个分支的终点都是一个节点 (node),每个节点都包含着对象 (objects)。DOM 的方法 (methods) 让你可以用特定方式操作这个树,用这些方法你可以改变文档的结构、样式或者内容。节点可以关联上事件处理器,一旦某一事件被触发了,那些事件处理器就会被执行。

这里还有一篇关于 DOM 的 介绍

DOM(Document Object Model)是将整个 HTML 文档的每一个标签元素视为一个对象,这个对象下包含了许多的属性和方法,通过操作这些属性或者调用这些方法实现对 HTML 的动态更新,为实现网页特效以及用户交互提供技术支撑。

在浏览器标签中当前载入的文档用文档对象模型来表示。这是一个由浏览器生成的 " 树结构 ",使编程语言可以很容易的访问 HTML 结构 — 例如浏览器自己在呈现页面时,使用它将样式和其他信息应用于正确的元素,而页面呈现完成以后,开发人员可以用 JavaScript 操作 DOM。

简言之 DOM 是用来动态修改 HTML 的 (浏览器提供的一套专门用来 操作网页内容 的功能),其目的是开发网页特效及用户交互

DOM 树

  • 将 HTML 文档以树状结构直观的表现出来,我们称之为文档树或 DOM 树
  • 描述网页内容关系的名词
  • 作用:文档树直观的体现了标签与标签之间的关系
html
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>标题</title>
  </head>
  <body>
    文本
    <a href="">链接名</a>
    <div id="" class="">文本</div>
  </body>
</html>

如下图所示,将 HTML 文档以树状结构直观的表现出来,我们称之为文档树或 DOM 树,文档树直观的体现了标签与标签之间的关系。

dom

DOM 节点

dom-object

  • DOM 对象:浏览器根据 html 标签生成的 JS 对象
    • 所有的标签属性都可以在这个对象上面找到
    • 修改这个对象的属性会自动映射到标签身上
  • DOM 的核心思想
    • 把网页内容当做对象来处理

节点是文档树的组成部分,每一个节点都是一个 DOM 对象,主要分为元素节点、属性节点、文本节点等。

  • 元素节点: 一个元素,存在于 DOM 中。
  • 根节点: 树中顶层节点,在 HTML 的情况下,总是一个 HTML 节点(其他标记词汇,如 SVG 和定制 XML 将有不同的根元素)。
  • 子节点: 直接位于另一个节点内的节点。例如上面例子中,IMGSECTION 的子节点。
  • 后代节点: 位于另一个节点内任意位置的节点。例如 上面例子中,IMGSECTION 的子节点,也是一个后代节点。IMG 不是 BODY 的子节点,因为它在树中低了 BODY 两级,但它是 BODY 的后代之一。
  • 父节点: 里面有另一个节点的节点。例如上面的例子中 BODYSECTION 的父节点。
  • 兄弟节点: DOM 树中位于同一等级的节点。例如上面例子中,IMGP 是兄弟。
  • 文本节点: 包含文字串的节点

document 对象

  • 是 DOM 里提供的一个对象
  • 所以它提供的属性和方法都是用来访问和操作网页内容的
    • 例:document.write()
  • 网页所有内容都在 document 里面

document 是 JavaScript 内置的专门用于 DOM 的对象,该对象包含了若干的属性和方法,document 是学习 DOM 的核心。

js
// document 是内置的对象
// console.log(typeof document);

// 1. 通过 document 获取根节点
console.log(document.documentElement); // 对应 html 标签

// 2. 通过 document 节取 body 节点
console.log(document.body); // 对应 body 标签

// 3. 通过 document.write 方法向网页输出内容
document.write("Hello World!");

上述列举了 document 对象的部分属性和方法,我们先对 document 有一个整体的认识。

获取 DOM 对象

查找元素 DOM 元素就是利用 JS 选择页面中标签元素。

  1. 获取一个 DOM 元素我们使用谁?能直接操作修改吗?
    • querySelector()
    • 可以
  2. 获取多个 DOM 元素我们使用谁?能直接修改吗?如果不能可以怎么做到修改?
    • querySelectorAll()
    • 不可以,只能通过遍历的方式一次给里面的元素做修改
  3. 获取页面中的标签我们最终常用那两种方式?
    • querySelectorAll()
    • querySelector()
  4. 他们两者的区别是什么?
    • querySelector() 只能选择一个元素,可以直接操作
    • querySelectorAll() 可以选择多个元素,得到的是伪数组,需要遍历得到每一个元素
  5. 他们两者小括号里面的参数有神马注意事项?
    • 里面写 css 选择器
    • 必须是字符串,也就是必须加引号
  • Document.querySelector() 满足条件的第一个元素(推荐的主流方法
  • Document.querySelectorAll() 满足条件的元素集合 返回伪数组
  • 了解其他方式
    • Document.getElementById(),选择一个 id 属性值已知的元素,例如 <p id="myId">My paragraph</p>。ID 作为参数传递给函数,即 var elementRef = document.getElementById('myId')
    • Document.getElementsByTagName(),返回页面中包含的所有已知类型元素的数组。如 <p>s, <a>。元素类型作为参数传递给函数,即 var elementRefArray = document.getElementsByTagName('p')

总结:

  • document.getElementById 专门获取元素类型节点,根据标签的 id 属性查找
  • 任意 DOM 对象都包含 nodeType 属性,用来检检测节点类型

Document.querySelector()

document.querySelector() - Web API 接口参考 | MDN (mozilla.org)

文档对象模型 Document 引用的 querySelector() 方法返回文档中与指定选择器或选择器组匹配的第一个 Element 对象。如果找不到匹配项,则返回 null

备注

匹配是使用深度优先先序遍历,从文档标记中的第一个元素开始,并按子节点的顺序依次遍历。

js
element = document.querySelector(selectors);

// selectors 包含一个或多个要匹配的选择器的 DOM 字符串 DOMString。该字符串必须是有效的 CSS 选择器字符串;如果不是,则引发 SYNTAX_ERR 异常。请参阅使用选择器定位 DOM 元素以获取有关选择器以及如何管理它们的更多信息。

// 返回值 表示文档中与指定的一组 CSS 选择器匹配的第一个元素,一个 Element 对象。如果没有匹配到,则返回 null。

// 如果您需要与指定选择器匹配的所有元素的列表,则应该使用 querySelectorAll() 。

// 异常 SYNTAX_ERR 指定 selectors 的语法无效。

备注

如果要匹配的 ID 或选择器不符合 CSS 语法(比如不恰当地使用了冒号或者空格),你必须用反斜杠将这些字符转义。由于 JavaScript 中,反斜杠是转义字符,所以当你输入一个文本串时,你必须将它转义两次(一次是为 JavaScript 字符串转义,另一次是为 querySelector 转义):

html
<div id="foo\bar"></div>
<div id="foo:bar"></div>

<script>
  console.log("#foo\bar"); // "#fooar"
  document.querySelector("#foo\bar"); // 不匹配任何元素

  console.log("#foo\\bar"); // "#foo\bar"
  console.log("#foo\\\\bar"); // "#foo\\bar"
  document.querySelector("#foo\\\\bar"); // 匹配第一个 div

  document.querySelector("#foo:bar"); // 不匹配任何元素
  document.querySelector("#foo\\:bar"); // 匹配第二个 div
</script>

Document.querySelectorAll()

Document.querySelectorAll - Web API 接口参考 | MDN (mozilla.org)

返回与指定的选择器组匹配的文档中的元素列表 (使用深度优先的先序遍历文档的节点)。返回的对象是 NodeList

NodeList

NodeList - Web API 接口参考 | MDN (mozilla.org)

NodeList (伪数组)对象是节点的集合,通常是由属性,如 Node.childNodes 和 方法,如 document.querySelectorAll 返回的。

  • 有长度有索引号的数组
  • 但是没有 pop() push() 等数组方法
  • 想要得到里面的每一个对象,则需要遍历(for)的方式获得。

NodeList 不是一个数组,是一个类似数组的对象 (Like Array Object)。虽然 NodeList 不是一个数组,但是可以使用 forEach() 来迭代。你还可以使用 Array.from() 将其转换为数组。

不过,有些浏览器较为过时,没有实现 NodeList.forEach()Array.from()。你可以用 Array.prototype.forEach() 来规避这一问题

js
elementList = parentNode.querySelectorAll(selectors);

// selectors 一个 DOMString 包含一个或多个匹配的选择器。这个字符串必须是一个合法的 CSS selector 如果不是,会抛出一个 SyntaxError 错误。有关使用选择器标识元素的更多信息,请参阅 Locating DOM elements using selectors 可以通过使用逗号分隔多个选择器来指定它们。

// 返回值 一个静态 NodeList,包含一个与至少一个指定选择器匹配的元素的 Element 对象,或者在没有匹配的情况下为空 NodeList

// SyntaxError 如果指定的 选择器 不合法,会抛出错误。如$("##div")

注意

  • 哪怕只有一个元素,通过 querySelectAll() 获取过来的也是一个伪数组,里面只有一个元素而已

  • 默认情况下,querySelectorAll() 仅验证选择器中的最后一个元素是否在搜索范围内。

    html
    <div class="outer">
      <div class="select">
        <div class="inner"></div>
      </div>
    </div>
    
    <script>
      var select = document.querySelector(".select");
      var inner = select.querySelectorAll(".outer .inner");
      inner.length; // 1, not 0!
    </script>

    在这个例子中,当在 <div> 上下文中选择带有 "select" 类的 ".outer .inner" 时,仍然会找到类 ".inner" 的元素,即使 .outer 不是基类的后代 执行搜索的元素(".select")。

    :scope 伪类符合预期的行为,只匹配基本元素后代的选择器:

    js
    var select = document.querySelector(".select");
    var inner = select.querySelectorAll(":scope .outer .inner");
    inner.length; // 0

Document.getElementById()

document.getElementById - Web API 接口参考 | MDN (mozilla.org)

Document 的方法 getElementById() 返回一个匹配特定 ID 的元素。由于元素的 ID 在大部分情况下要求是独一无二的,这个方法自然而然地成为了一个高效查找特定元素的方法。

如果需要查找到那些没有 ID 的元素,你可以考虑通过 CSS 选择器使用 querySelector()

js
var element = document.getElementById(id);

// element 是一个 Element 对象。如果当前文档中拥有特定 ID 的元素不存在则返回 null。
// id 是大小写敏感的字符串,代表了所要查找的元素的唯一 ID。
// 返回值 返回一个匹配到 ID 的 DOM Element 对象。若在当前 Document 下没有找到,则返回 null。
html
<!-- 点击颜色按钮给文字设置颜色 -->

<p id="para">Some text here</p>
<button onclick="changeColor('blue');">blue</button>
<button onclick="changeColor('red');">red</button>

<script>
  function changeColor(newColor) {
    var elem = document.getElementById("para");
    elem.style.color = newColor;
  }
</script>

Document.getElementsByTagName()

Document.getElementsByTagName() - Web API 接口参考 | MDN (mozilla.org)

返回一个包括所有给定标签名称的元素的 HTML 集合 HTMLCollection。整个文件结构都会被搜索,包括根节点。返回的 HTML 集合 是动态的,意味着它可以自动更新自己来保持和 DOM 树的同步而不用再次调用 document.getElementsByTagName()

js
var elements = document.getElementsByTagName(name);

// elements 是一个由发现的元素出现在树中的顺序构成的动态的 HTML 集合 HTMLCollection (但是看下面的提示) 。
// name 是一个代表元素的名称的字符串。特殊字符 "*" 代表了所有元素。
案例练习

请控制台依次输出 3 个 li 的 DOM 对象。

html
<ul class="nav">
  <li>我的首页</li>
  <li>产品介绍</li>
  <li>联系方式</li>
</ul>
js
const lis = document.querySelectorAll("li");
for (let i = 0; i < lis.length; i++) {
  console.log(lis[i]);
}
js
const lis = document.querySelectorAll("li");
for (let li of lis) {
  console.log(li);
}
js
const lis = document.querySelectorAll("li");
for (let index in lis) {
  console.log(lis[index]);
}
js
const lis = document.querySelectorAll("li");
for (let li of Array.from(lis)) {
  console.log(li);
}

操作元素内容

  1. 设置/修改 DOM 元素内容有哪 2 种方式?
    • 元素.innerText 属性
    • 元素.innerHTML 属性
  2. 三者的区别是什么?
    • 元素 .innerText 属性 只识别文本,不能解析标签
    • 元素 .innerHTML 属性 能识别文本,能够解析标签
    • 如果还在纠结到底用谁,你可以选择 innerHTML

DOM 对象都是根据标签生成的,所以操作标签,本质上就是操作 DOM 对象。如果想要修改标签元素的里面的内容,则可以使用如下几种方式:

  • 对象 .innerText 属性
  • 对象 .innerHTML 属性

对象 .innerText 属性

  • 将文本内容添加/更新到任意标签位置
  • 显示纯文本,不解析标签
js
// innerText 将文本内容添加/更新到任意标签位置
const intro = document.querySelector(".intro");
// intro.innerText = '嗨~ 我叫李雷!'
// intro.innerText = '<h4>嗨~ 我叫李雷!</h4>'

对象 .innerHTML 属性

  • 将文本内容添加/更新到任意标签位置
  • 会解析标签,多标签建议使用模板字符
js
// innerHTML 将文本内容添加/更新到任意标签位置
const intro = document.querySelector(".intro");
intro.innerHTML = "嗨~ 我叫韩梅梅!";
intro.innerHTML = `<h4>嗨~ 我叫韩梅梅!</h4>`;

总结

如果文本内容中包含 html 标签时推荐使用 innerHTML,否则建议使用 innerText 属性。

案例练习 - 年会抽奖案例

传智教育年会抽奖 (codepen.io)

  • 需求:从数组随机抽取一等奖、二等奖和三等奖,显示到对应的标签里面。

  • 素材:

    • html 文件结构
    • 数组名单 ' 周杰伦 ', ' 刘德华 ', ' 周星驰 ', 'Pink 老师 ', ' 张学友 '
  • 分析:

    • 声明数组:const personArr = ['周杰伦', '刘德华', '周星驰', 'Pink老师', '张学友']
    • 一等奖:随机生成一个数字(0~数组长度),找到对应数组的名字
    • 通过 innerText 或者 innerHTML 将名字写入 span 元素内部
    • 二等奖依次类推
html
<div class="wrapper">
  <div class="title">传智教育年会抽奖</div>
  <div class="info">
    <h1>一等奖:<span id="one">???</span></h1>
    <h3>二等奖:<span id="two">???</span></h3>
    <h5>三等奖:<span id="three">???</span></h5>
  </div>
</div>
css
.wrapper {
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  flex-wrap: nowrap;

  width: 650px;
  height: 500px;
  margin: 20px auto;
  /* background: linear-gradient(45deg, #fbb017, #e55d2d); */
  background-color: #e55d2d;
  color: #f5d894;
  border: 10px solid #fbb017;
  border-radius: 6px;
  box-shadow: 0 0 10px rgba(0, 0, 0, 0.5);
  box-sizing: border-box;
}

.wrapper .title {
  font-size: 26px;
  font-weight: 700;
}

.info h1 {
  font-size: 24px;
  font-weight: normal;
}

.info h3 {
  font-size: 20px;
  font-weight: normal;
}

.info h5 {
  font-size: 16px;
  font-weight: normal;
}
js
const personArr = ["周杰伦", "刘德华", "周星驰", "Pink 老师", "张学友"];
let remainingPerson = [...personArr];

function getRandomPerson() {
  const index = Math.floor(Math.random() * remainingPerson.length);
  const person = remainingPerson[index];
  remainingPerson.splice(index, 1);
  return person;
}

function showPerson() {
  const oneInfo = document.querySelector("#one");
  const twoInfo = document.querySelector("#two");
  const threeInfo = document.querySelector("#three");

  const one = getRandomPerson();
  const two = getRandomPerson();
  const three = getRandomPerson();

  oneInfo.innerHTML = one;
  twoInfo.innerHTML = two;
  threeInfo.innerHTML = three;
}

showPerson();

操作元素属性

操作元素常用属性

  • 还可以通过 JS 设置/修改标签元素属性,比如通过 src 更换 图片
  • 最常见的属性比如: hreftitlesrc
js
对象.属性 =;

// 1. 获取 img 对应的 DOM 元素
const pic = document.querySelector(".pic");
// 2. 修改属性
pic.src = "./images/lion.webp";
pic.width = 400;
pic.alt = "图片不见了...";
案例练习 - 页面刷新,图片随机更换

页面刷新,图片随机更换 (codepen.io)

  • 需求:当我们刷新页面,页面中的图片随机显示不同的图片
  • 分析:
    • 随机显示,则需要用到随机函数
    • 更换图片需要用到图片的 src 属性,进行修改
    • 核心思路:
      • 获取图片元素
      • 随机得到图片序号
      • 图片 .src = 图片随机路径
html
<img src="https://source.unsplash.com/400x175/?sports" alt="" />
js
function getRandom(N, M) {
  return Math.floor(Math.random() * (M - N + 1)) + N;
}

// 随机生成 11 位字母数字组合 //qO-PIF84Vxg qO-PIF84Vxg uran4t5LlvI
function getRandomStr() {
  const str = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-";
  let result = "";
  for (let i = 0; i < 11; i++) {
    const random = getRandom(0, str.length - 1);
    result += str[random];
  }
  return result;
}

const img = document.querySelector("img");
// const random = getRandomStr()
const random = getRandom(5, 10) * 100 + "x" + getRandom(5, 10) * 100;
img.src = `https://source.unsplash.com/${random}/?sports`;
img.alt = `随机图片 ${random}`;
img.title = `随机图片 ${random}`;

console.log(random);

操作元素样式属性

  1. 设置/修改元素样式属性通过 style 属性引出来
  2. 如果需要修改一个 div 盒子的样式,比如 padding-left, 如何写?
    • element.style.paddingLeft = ‘300px’
    • 小驼峰命名法
  3. 因为我们是样式属性,一定别忘记,大部分数字后面都需要加单位。
  4. 使用 className 有什么好处?
    • 可以同时修改多个样式
  5. 使用 className 有什么注意事项?
    • 直接使用 className 赋值会覆盖以前的类名
  6. 使用 classNameclassList 的区别?
    • 修改大量样式的更方便
    • 修改不多样式的时候方便
    • classList 是追加和删除不影响以前类名
  • 通过 JS 设置/修改标签元素的样式属性。
  • 比如通过 轮播图小圆点自动更换颜色样式
  • 点击按钮可以滚动图片,这是移动的图片的位置 left 等等

通过 style 属性操作 CSS

  • 应用【修改样式】,通过修改行内样式 style 属性,实现对样式的动态修改。
  • 通过元素节点获得的 style 属性本身的数据类型也是对象,如 box.style.colorbox.style.width 分别用来获取元素节点 CSS 样式的 colorwidth 的值。
  • 任何标签都有 style 属性,通过 style 属性可以动态更改网页标签的样式,如要遇到 css 属性中包含字符 - 时,要将 - 去掉并将其后面的字母改成大写,如 background-color 要写成 box.style.backgroundColor
js
对象.style.样式属性 =;
html
<div class="intro">随便一些文本内容</div>
<script>
  // 获取 DOM 节点
  const box = document.querySelector(".intro");
  box.style.color = "red";
  box.style.width = "300px";
  // css 属性的 - 连接符与 JavaScript 的 减运算符
  // 冲突,所以要改成驼峰法
  box.style.backgroundColor = "pink";
</script>

注意

  1. 修改样式通过 style 属性引出
  2. 如果属性有 - 连接符,需要转换为小驼峰命名法
  3. 赋值的时候,需要的时候不要忘记加css 单位
案例练习 - 页面刷新,页面随机更换背景图片

页面刷新,页面随机更换背景图片 (codepen.io)

  • 需求:当我们刷新页面,页面中的背景图片随机显示不同的图片
  • 分析:
    • 随机函数
    • css 页面背景图片 background-image
    • 标签选择 body,因为 body 是唯一的标签,可以直接写 document.body.style
js
function getRandom(N, M) {
  return Math.floor(Math.random() * (M - N + 1)) + N;
}

// 随机生成 11 位字母数字组合 qO-PIF84Vxg qO-PIF84Vxg uran4t5LlvI
function getRandomStr() {
  const str = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-";
  let result = "";
  for (let i = 0; i < 11; i++) {
    const random = getRandom(0, str.length - 1);
    result += str[random];
  }
  return result;
}

const random = getRandom(5, 10) * 100 + "x" + getRandom(5, 10) * 100;
// document.body.style.backgroundImage = `url(https://source.unsplash.com/${random}/?nature,water)`;
document.body.style.background = `url(https://source.unsplash.com/${random}/?nature,water) no-repeat center center fixed`;

console.log(random);

操作类名 className 操作 CSS

  • 如果修改的样式比较多,直接通过 style 属性修改比较繁琐,我们可以通过借助于 css 类名的形式。
js
// active 是一个 css 类名
元素.className = "active";

注意

  1. 由于 class 是关键字,所以使用 className 去代替
  2. className 是使用新值换旧值,如果需要添加一个类,需要保留之前的类名
html
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>Document</title>
    <style>
      div {
        width: 200px;
        height: 200px;
        background-color: pink;
      }

      .nav {
        color: red;
      }

      .box {
        width: 300px;
        height: 300px;
        background-color: skyblue;
        margin: 100px auto;
        padding: 10px;
        border: 1px solid #000;
      }
    </style>
  </head>

  <body>
    <div class="nav">123</div>
    <script>
      // 1. 获取元素
      const div = document.querySelector("div");
      // 2.添加类名  class 是个关键字 我们用 className
      div.className = "nav box";
    </script>
  </body>
</html>

通过 classList 操作类控制 CSS

  • 为了解决 className 容易覆盖以前的类名,我们可以通过 classList 方式追加和删除类名
js
// 追加一个类
元素.classList.add("类名");
// 删除一个类
元素.classList.remove("类名");
// 切换一个类
元素.classList.toggle("类名");
html
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>Document</title>
    <style>
      .box {
        width: 200px;
        height: 200px;
        color: #333;
      }

      .active {
        color: red;
        background-color: pink;
      }
    </style>
  </head>

  <body>
    <div class="box active">文字</div>
    <script>
      // 通过 classList 添加
      // 1. 获取元素
      const box = document.querySelector(".box");
      // 2. 修改样式
      // 2.1 追加类 add() 类名不加点,并且是字符串
      // box.classList.add('active')
      // 2.2 删除类  remove() 类名不加点,并且是字符串
      // box.classList.remove('box')
      // 2.3 切换类  toggle()  有还是没有啊,有就删掉,没有就加上
      box.classList.toggle("active");
    </script>
  </body>
</html>
案例练习 - 轮播图随机版

随机轮播图 (codepen.io)

  • 需求:当我们刷新页面,页面中的轮播图会显示不同图片以及样式
  • 模块:
    • 图片会随机变换
    • 底部盒子背景颜色和文字内容会变换
    • 小圆点随机一个高亮显示
  • 分析:
    • 准备一个数组对象,里面包含详细信息(素材包含)
    • 随机选择一个数字,选出数组对应的对象,更换图片,底部盒子背景颜色,以及文字内容
    • 利用这个随机数字,让小圆点添加高亮的类(addClass)利用 css 结构伪类选择器
js
// 1. 初始数据
const sliderData = [
  {url: "./images/slider01.jpg", title: "对人类来说会不会太超前了?", color: "rgb(100, 67, 68)"},
  {url: "./images/slider02.jpg", title: "开启剑与雪的黑暗传说!", color: "rgb(43, 35, 26)"},
  {url: "./images/slider03.jpg", title: "真正的 jo 厨出现了!", color: "rgb(36, 31, 33)"},
  {url: "./images/slider04.jpg", title: "李玉刚:让世界通过 B 站看到东方大国文化", color: "rgb(139, 98, 66)"},
  {url: "./images/slider05.jpg", title: "快来分享你的寒假日常吧~", color: "rgb(67, 90, 92)"},
  {url: "./images/slider06.jpg", title: "哔哩哔哩小年 YEAH", color: "rgb(166, 131, 143)"},
  {url: "./images/slider07.jpg", title: "一站式解决你的电脑配置问题!!!", color: "rgb(53, 29, 25)"},
  {url: "./images/slider08.jpg", title: "谁不想和小猫咪贴贴呢!", color: "rgb(99, 72, 114)"},
];
html
<div class="carousel">
  <div class="carousel-container">
    <!-- <div class="carousel-item active">
        <img src="https://img1.baidu.com/it/u=1831000000,1831000000&fm=26&fmt=auto&gp=0.jpg" alt="" />
      </div> -->
  </div>
  <div class="carousel-footer">
    <div class="carousel-mask"></div>
    <div class="carousel-tool">
      <a href="https://www.bilibili.com/bangumi/play/ep743775" target="_blank" rel="noopener noreferrer">
        <span>XY 密室今日开业!游戏开始!</span>
      </a>
      <ul>
        <li></li>
        <li></li>
        <li></li>
        <li></li>
        <li></li>
        <li></li>
        <li></li>
        <li></li>
      </ul>
    </div>
    <div class="carousel-buttons">
      <button class="prev">&lt;</button>
      <button class="next">&gt;</button>
    </div>
  </div>
</div>
css
.carousel {
  width: 1000px;
  height: 630px;
  margin: 0 auto;
  overflow: hidden;
  position: relative;
}

.carousel .carousel-container {
  width: 100%;
  height: 550px;
  position: relative;
}

.carousel .carousel-container .carousel-item {
  width: 100%;
  height: 100%;
  position: absolute;
  top: 0;
  left: 0;
}

.carousel .carousel-container .carousel-item img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.carousel .carousel-footer {
  width: 100%;
  height: 80px;
  position: relative;
  padding: 12px 12px 0 12px;
}

.carousel .carousel-footer .carousel-mask {
  width: 100%;
  height: 100%;
  position: absolute;
  top: 0;
  left: 0;
  background: linear-gradient(transparent, rgba(0, 0, 0, 0.8));
}

.carousel .carousel-footer .carousel-tool a {
  margin: 0;
  color: #fff;
  font-size: 18px;
  margin-bottom: 10px;
  text-decoration: none;
}

.carousel .carousel-footer .carousel-tool a:hover {
  text-decoration: underline;
}

.carousel .carousel-footer .carousel-tool ul {
  margin: 0;
  padding: 0;
  list-style: none;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
}

.carousel .carousel-footer .carousel-tool ul li {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background-color: #fff;
  margin: 10px 5px 0 5px;

  opacity: 0.4;
  cursor: pointer;
}

.carousel .carousel-footer .carousel-tool ul li.active {
  width: 12px;
  height: 12px;
  opacity: 1;
  /* background-color: #ff0000; */
}

.carousel .carousel-footer .carousel-buttons {
  position: absolute;
  right: 0;
  top: 12px;
  display: flex;
}

.carousel .carousel-footer .carousel-buttons button {
  width: 30px;
  height: 30px;
  margin-right: 40px;
  border-radius: 5px;
  border: none;
  background-color: rgba(255, 255, 255, 0.1);
  color: #fff;
  font-size: 20px;
  cursor: pointer;
  outline: none;
}

.carousel .carousel-footer .carousel-buttons button:hover {
  background-color: rgba(255, 255, 255, 0.2);
}
js
const sliderData = [
  {
    url: "https://source.unsplash.com/976x550/?stars,night,sky,water",
    title: "对人类来说会不会太超前了?",
    color: "rgb(100, 67, 68)",
  },
  {
    url: "https://source.unsplash.com/976x550/?stars,night,sky,water",
    title: "开启剑与雪的黑暗传说!",
    color: "rgb(43, 35, 26)",
  },
  {
    url: "https://source.unsplash.com/976x550/?stars,night,sky,water",
    title: "真正的 jo 厨出现了!",
    color: "rgb(36, 31, 33)",
  },
  {
    url: "https://source.unsplash.com/976x550/?stars,night,sky,water",
    title: "李玉刚:让世界通过 B 站看到东方大国文化",
    color: "rgb(139, 98, 66)",
  },
  {
    url: "https://source.unsplash.com/976x550/?stars,night,sky,water",
    title: "快来分享你的寒假日常吧~",
    color: "rgb(67, 90, 92)",
  },
  {
    url: "https://source.unsplash.com/976x550/?stars,night,sky,water",
    title: "哔哩哔哩小年 YEAH",
    color: "rgb(166, 131, 143)",
  },
  {
    url: "https://source.unsplash.com/976x550/?stars,night,sky,water",
    title: "一站式解决你的电脑配置问题!!!",
    color: "rgb(53, 29, 25)",
  },
  {
    url: "https://source.unsplash.com/976x550/?stars,night,sky,water",
    title: "谁不想和小猫咪贴贴呢!",
    color: "rgb(99, 72, 114)",
  },
];

const random = parseInt(Math.random() * sliderData.length);
const sliderContainer = document.querySelector(".carousel .carousel-container");

let sliderItem = document.createElement("div");
sliderItem.className = "carousel-item";
sliderItemImg = document.createElement("img");
sliderItemImg.src = sliderData[random].url;
sliderItem.appendChild(sliderItemImg);
sliderContainer.appendChild(sliderItem);

const sliderFooter = document.querySelector(".carousel .carousel-footer");
sliderFooter.style.backgroundColor = sliderData[random].color;

const sliderTool = document.querySelector(".carousel .carousel-tool");
sliderTool.querySelector("span").innerText = sliderData[random].title;

const sliderToolDot = sliderTool.querySelector(`ul li:nth-child(${random + 1})`);
sliderToolDot.className = "active";

操作 表单元素 属性

  • 表单很多情况,也需要修改属性,比如点击眼睛,可以看到密码,本质是把表单类型转换为文本框
  • 正常的有属性有取值的 跟其他的标签属性没有任何区别
  • 获取:DOM对象.属性名
  • 设置:DOM对象.属性名 = 新值
  • 表单属性中添加就有效果,移除就没有效果,一律使用布尔值表示 如果为 true 代表添加了该属性,如果是 false 代表移除了该属性
    • 比如: disabledcheckedselected
html
<input type="text" value="请输入" />
<button disabled>按钮</button>
<input type="checkbox" name="" id="" class="agree" />
<script>
  // 1. 获取元素
  let input = document.querySelector("input");
  // 2. 取值或者设置值  得到 input 里面的值可以用 value
  // console.log(input.value)
  input.value = "小米手机";
  input.type = "password";

  // 2. 启用按钮
  let btn = document.querySelector("button");
  // disabled 不可用   =  false  这样可以让按钮启用
  btn.disabled = false;
  // 3. 勾选复选框
  let checkbox = document.querySelector(".agree");
  checkbox.checked = false;
</script>

自定义属性

  • 标准属性:标签天生自带的属性 比如 classidtitle 等,可以直接使用点语法操作比如:disabledcheckedselected
  • 自定义属性:
    • 在 html5 中推出来了专门的 data-自定义属性
    • 在标签上一律以 data-开头
    • 在 DOM 对象上一律以 dataset 对象方式获取
html
<div data-id="1">自定义属性</div>
<script>
  // 1. 获取元素
  let div = document.querySelector("div");
  // 2. 获取自定义属性值
  console.log(div.dataset.id);
</script>

定时器 - 间歇函数

  1. 定时器函数有什么作用?

    • 可以根据时间自动重复执行某些代码
  2. 定时器函数如何开启?

    • setInterval(函数名, 时间)
  3. 定时器函数如何关闭?

    js
    let 变量名 = setInterval(函数间隔时间);
    clearInterval(变量名);

定时器函数介绍

  • 网页中经常会需要一种功能:每隔一段时间需要自动执行一段代码,不需要我们手动去触发
    • 例如:网页中的倒计时。要实现这种需求,需要定时器函数
  • 定时器函数是一种能够在指定时间间隔后执行指定函数的机制。JavaScript 中有两种类型的定时器函数:延迟函数 setTimeout 和 间歇函数 setInterval
    • 间歇函数 setIntervalsetInterval 函数可以每隔一段时间执行一次函数。
    • 延迟函数 setTimeoutsetTimeout 函数可以在指定的时间后执行一次函数。
  • 如果你想取消一个定时器,可以使用 clearTimeoutclearInterval 函数。
    • clearTimeout 可以取消 setTimeout 函数创建的定时器。
    • clearInterval 可以取消 setInterval 函数创建的定时器。

定时器间歇函数基本使用

开启定时器

js
setInterval(function, milliseconds)
// 第一个参数 function 是要执行的函数
// 第二个参数 milliseconds 是时间间隔,以毫秒为单位。
// 该函数会在每个时间间隔内重复执行。
js
// 每隔 1 秒钟输出一次“Hello World!”
setInterval(function () {
  console.log("Hello World!");
}, 1000);

注意

  1. 函数名字不需要加括号。
  2. setInterval() 函数返回一个计时器 ID,可以用来取消计时器。
  3. 间歇函数会一直重复执行,直到调用 clearInterval 方法停止它。因此,需要根据实际情况来判断何时停止间歇函数的执行
js
function repeat() {
  console.log("Hello World!");
}

// 每隔 1 秒钟输出一次“Hello World!”
setInterval(repeat, 1000);

间歇函数缺点

  • 如果函数执行的时间比时间间隔还长,那么函数会在上一个执行完成之前再次执行,可能会导致函数重复执行的问题。此外,间歇函数也可能会在浏览器的后台标签中被暂停,导致函数的执行不准确。
  • setInterval() 函数会一直执行,直到计时器被取消或页面被关闭。因此,使用 setInterval() 函数时需要小心,避免出现性能问题。
  • 如果要停止计时器,请使用 clearInterval() 函数。

关闭定时器

  • clearInterval() 函数用于停止由 setInterval() 函数启动的周期性执行的代码。
  • setInterval() 函数会重复执行指定的代码,直到 clearInterval() 函数被调用或窗口被关闭。如果您想停止周期性执行的代码,可以使用 clearInterval() 函数。
js
clearInterval(intervalID);
// intervalID:必需,周期性执行的代码的标识符,由 setInterval() 函数返回。
js
// 使用 setInterval() 创建了一个定时器,并将它的 ID 存储在变量 timerId 中
var timerId = setInterval(function () {
  console.log("定时器执行");
}, 1000);

// 使用 clearInterval() 方法来关闭这个定时器
clearInterval(timerId);
案例练习 - 阅读注册协议

阅读注册协议 (codepen.io)

  • 需求:按钮 60 秒之后才可以使用
  • 分析:
    • 开始先把按钮禁用(disabled 属性)
    • 一定要获取元素
    • 函数内处理逻辑
      • 秒数开始减减
      • 按钮里面的文字跟着一起变化
      • 如果秒数等于 0 停止定时器 里面文字变为 同意 最后 按钮可以点击
html
<div class="protocal">
  <label>
    <h2>百度用户协议</h2>
    <textarea>

特别提示:
您在使用百度公司提供的各项服务之前,请您务必审慎阅读、充分理解本协议各条款内容,特别是以粗体标注的部分,包括但不限于免除或者限制责任的条款。如您不同意本服务协议及/或随时对其的修改,您可以主动停止使用百度公司提供的服务;您一旦使用百度公司提供的服务,即视为您已了解并完全同意本服务协议各项内容,包括百度公司对服务协议随时所做的任何修改,并成为我们的用户。

一、总则
1. 用户可以使用百度各个频道、产品的单项服务,当用户使用百度各单项服务时,用户的使用行为视为其对该单项服务的服务条款以及百度在该单项服务中发出的各类公告的同意。
2. 百度用户协议以及各个频道、产品单项服务条款和公告可由百度公司随时更新,且无需另行通知。您在使用相关服务时,应关注并遵守其所适用的相关条款。

二、帐号注册与使用
您使用部分百度产品或服务时需要注册百度帐号,当您注册和使用百度帐号时应遵守下述要求:

1. 用户注册成功后,百度公司将给予每个用户一个用户帐号,用户可以自主设置帐号密码。该用户帐号和密码由用户负责保管;用户应当对以其用户帐号进行的所有活动和事件负法律责任。
2. 您按照注册页面提示填写信息、阅读并同意本协议且完成全部注册程序后,除百度平台的具体产品对帐户有单独的注册要求外,您可获得百度平台(baidu.com 网站及客户端)帐号并成为百度用户,可通过百度帐户使用百度平台的各项产品和服务。
3. 为了方便您在百度产品中享有一致性的服务,如您已经在某一百度产品中登录百度帐号,在您首次使用其他百度产品时可能同步您的登录状态。此环节并不会额外收集、使用您的个人信息。如您想退出帐号登录,可在产品设置页面退出登录。
4. 百度帐号(即百度用户 ID)的所有权归百度公司,用户按注册页面引导填写信息,阅读并同意本协议且完成全部注册程序后,即可获得百度帐号并成为用户。用户应提供及时、详尽及准确的个人资料,并不断更新注册资料,符合及时、详尽准确的要求。所有原始键入的资料将引用为注册资料。如果因注册信息不真实或更新不及时而引发的相关问题,百度公司不负任何责任。您可以通过百度帐号设置页面查询、更正您的信息,百度帐号设置页面地址:https://passport.baidu.com/center 用户应当通过真实身份信息认证注册帐号,且用户提交的帐号名称、头像和简介等注册信息中不得出现违法和不良信息,经百度公司审核,如存在上述情况,百度公司将不予注册;同时,在注册后,如发现用户以虚假信息骗取帐号名称注册,或其帐号头像、简介等注册信息存在违法和不良信息的,百度公司有权不经通知单方采取限期改正、暂停使用、注销登记、收回等措施。
5. 基于国家法律法规的规定要求或者百度服务政策的要求,您在使用某些百度服务时需要填写真实的姓名、身份证号等信息。若您填写的信息不完整、不正确,则可能无法使用相应服务或在使用过程中受到限制。
6. 当您使用刷脸登录、验证功能时,我们会收集并保存您的人脸照片和信息。您应按照页面的提示完成相应动作,百度系统判断您的动作符合要求后,即可完成脸部拍摄。您同意仅限您本人为您本人所持有的百度账户使用刷脸登录、验证服务。为了提高验证的准确性,您同意百度可在必要时将您向百度提供的人脸照片、脸部特征与法律法规允许的机构或政府机关授权的机构所保存的您的脸部特征进行比对核验。
7. 百度帐号包括帐户名称和密码,您可使用设置的帐户名称(包括用户名、手机号、邮箱)和密码登录;在帐号使用过程中,为了保障您的帐号安全基于不同的终端以及您的使用习惯,我们可能采取不同的验证措施识别您的身份。例如您的帐户在新设备首次登录,我们可能通过密码加校验码的方式识别您的身份,验证方式包括但不限于短信验证码、服务使用信息验证。
8. 用户不应将其帐号、密码转让、出售或出借予他人使用,帐号使用权仅属于初始申请注册人,用户应对帐号内的全部行为独立承担全部责任。如果百度可合理判断相关帐号存在帐号交易情况、帐号被盗风险,百度有权独立采取冻结帐号、封禁帐号等措施。如因帐号转让、买卖或出借导致帐号纠纷或百度无法判断帐号归属,百度有权拒绝提供帐号找回,一旦发生该等举报或投诉,百度有权立即对帐号进行冻结或封禁。您应该妥善保管您的帐号和密码,您不应轻信借款、索要密码或其他涉及财产的网络信息,不应对外透露您的百度帐号、密码、百度向您发送的验证码信息或在非百度网站输入百度帐号、密码、验证码信息或在非百度网站通过二维码扫描等方式进行登录操作,否则可能导致您的帐号泄露及财产受损,上述因您帐号保管不当造成的损失将由您自行承担,同时,当发生上述情形且当百度无法合理判断帐号归属时,将无法为您找回帐号。
9. 因您个人原因导致的帐号无法使用,如需找回百度帐号,请按照百度帐号找回要求提供相应的信息,并确保提供的信息合法真实有效,若存在以下情形之一:(1)提供的信息不符合要求(2)帐号的唯一凭证不再有效(3)无法通过百度公司安全验证(4)存在帐号被盗等安全风险,或存在其他导致无法判断帐号归属的情形,百度公司有权拒绝提供帐号找回服务,并视情况采取冻结帐号、封禁帐号等措施。您百度帐号下绑定的手机号如因您个人原因停止使用,请尽快更改百度帐号的绑定手机,否则如遇运营商二次放号,后续使用该手机号的用户可以通过您原来的手机号进行验证登录,可查看到您的全部帐号信息及产品使用信息,您的百度帐号将无法继续使用,帐号内个人信息也有泄露的风险,请您务必及时更换绑定手机,如因未及时换绑手机号导致帐号丢失或个人信息泄露的责任由您自行承担,百度公司有权拒绝提供帐号找回服务,并视情况采取冻结帐号、封禁帐号等措施。

  </textarea
    >
  </label>
</div>
<button class="btn" disabled>我已经阅读用户协议 (10)</button>
css
.protocal textarea {
  width: 600px;
  height: 500px;
  resize: none;
  /* border: none; */
  outline: none;
  font-size: 16px;
  line-height: 1.5;
}

.btn {
  height: 24px;
  line-height: 24px;
  outline: none;
  font-size: 16px;
  cursor: pointer;
}
js
const btn = document.querySelector(".btn");

let count = 10;
let timer = setInterval(() => {
  count--;
  btn.innerHTML = `我已经阅读用户协议 (${count})`;
  if (count === 0) {
    clearInterval(timer);
    btn.innerHTML = "我已经阅读用户协议";
    btn.disabled = false;
  }
}, 1000);

综合案例 - 轮播图定时器版

轮播图定时器版 (codepen.io)

  • 需求:每隔一秒钟切换一个图片
  • 分析:
    • 准备一个数组对象,里面包含详细信息(素材包含)
    • 获取元素
    • 设置定时器函数
      • 设置一个变量 ++
      • 找到变量对应的对象
      • 更改图片、文字信息
      • 激活小圆点:移除上一个高亮的类名,当前变量对应的小圆点添加类
    • 处理图片自动复原从头播放(放到变量 ++ 后面,紧挨)
      • 如果图片播放到最后一张,就是大于等于数组的长度
      • 则把变量重置为 0
html
<div class="carousel">
  <div class="carousel-container">
    <!-- <div class="carousel-item active">
        <img src="https://img1.baidu.com/it/u=1831000000,1831000000&fm=26&fmt=auto&gp=0.jpg" alt="" />
      </div> -->
  </div>
  <div class="carousel-footer">
    <div class="carousel-mask"></div>
    <div class="carousel-tool">
      <a href="https://www.bilibili.com/bangumi/play/ep743775" target="_blank" rel="noopener noreferrer">
        <span>XY 密室今日开业!游戏开始!</span>
      </a>
      <ul>
        <li></li>
        <li></li>
        <li></li>
        <li></li>
        <li></li>
        <li></li>
        <li></li>
        <li></li>
      </ul>
    </div>
    <div class="carousel-buttons">
      <button class="prev">&lt;</button>
      <button class="next">&gt;</button>
    </div>
  </div>
</div>
css
.carousel {
  width: 1000px;
  height: 630px;
  margin: 0 auto;
  overflow: hidden;
  position: relative;
}

.carousel .carousel-container {
  width: 100%;
  height: 550px;
  position: relative;
}

.carousel .carousel-container .carousel-item {
  width: 100%;
  height: 100%;
  position: absolute;
  top: 0;
  left: 0;
}

.carousel .carousel-container .carousel-item img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.carousel .carousel-footer {
  width: 100%;
  height: 80px;
  position: relative;
  padding: 12px 12px 0 12px;
}

.carousel .carousel-footer .carousel-mask {
  width: 100%;
  height: 100%;
  position: absolute;
  top: 0;
  left: 0;
  background: linear-gradient(transparent, rgba(0, 0, 0, 0.8));
}

.carousel .carousel-footer .carousel-tool a {
  margin: 0;
  color: #fff;
  font-size: 18px;
  margin-bottom: 10px;
  text-decoration: none;
}

.carousel .carousel-footer .carousel-tool a:hover {
  text-decoration: underline;
}

.carousel .carousel-footer .carousel-tool ul {
  margin: 0;
  padding: 0;
  list-style: none;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
}

.carousel .carousel-footer .carousel-tool ul li {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background-color: #fff;
  margin: 10px 5px 0 5px;

  opacity: 0.4;
  cursor: pointer;
}

.carousel .carousel-footer .carousel-tool ul li.active {
  width: 12px;
  height: 12px;
  opacity: 1;
  /* background-color: #ff0000; */
}

.carousel .carousel-footer .carousel-buttons {
  position: absolute;
  right: 0;
  top: 12px;
  display: flex;
}

.carousel .carousel-footer .carousel-buttons button {
  width: 30px;
  height: 30px;
  margin-right: 40px;
  border-radius: 5px;
  border: none;
  background-color: rgba(255, 255, 255, 0.1);
  color: #fff;
  font-size: 20px;
  cursor: pointer;
  outline: none;
}

.carousel .carousel-footer .carousel-buttons button:hover {
  background-color: rgba(255, 255, 255, 0.2);
}
js
const sliderData = [
  {
    url: "https://i1.hdslb.com/bfs/archive/81584c757a6b4c71dc93170664745167b175dd72.jpg@976w_550h_1c.webp",
    title: "对人类来说会不会太超前了?",
    color: "rgb(100, 67, 68)",
  },
  {
    url: "https://i1.hdslb.com/bfs/archive/66faf0db298d9e61228283628064212e4c3152f8.jpg@976w_550h_1c.webp",
    title: "开启剑与雪的黑暗传说!",
    color: "rgb(43, 35, 26)",
  },
  {
    url: "https://i1.hdslb.com/bfs/archive/2a3b28a76cdbe112df557b453600e4c1c98bd3d0.jpg@976w_550h_1c.webp",
    title: "真正的 jo 厨出现了!",
    color: "rgb(36, 31, 33)",
  },
  {
    url: "https://i0.hdslb.com/bfs/archive/d7a15ce7bccdaf3ff0a41e2b2c80feac1f9edc82.jpg@976w_550h_1c.webp",
    title: "李玉刚:让世界通过 B 站看到东方大国文化",
    color: "rgb(139, 98, 66)",
  },
  {
    url: "https://i2.hdslb.com/bfs/archive/2f64c176864bced7835d375c47c2de700a107f93.jpg@976w_550h_1c.webp",
    title: "快来分享你的寒假日常吧~",
    color: "rgb(67, 90, 92)",
  },
  {
    url: "https://i2.hdslb.com/bfs/archive/b1a07a8baefa0694760cd6d99f212bee45d1333d.jpg@976w_550h_1c.webp",
    title: "哔哩哔哩小年 YEAH",
    color: "rgb(166, 131, 143)",
  },
  {
    url: "https://i0.hdslb.com/bfs/archive/67c3bbae0eec01a1783f35851649724cb4c408bd.jpg@976w_550h_1c.webp",
    title: "一站式解决你的电脑配置问题!!!",
    color: "rgb(53, 29, 25)",
  },
  {
    url: "https://i1.hdslb.com/bfs/archive/55ce9a4d1797ec56a0d4ed727f1a279b89ec3664.jpg@976w_550h_1c.webp",
    title: "谁不想和小猫咪贴贴呢!",
    color: "rgb(99, 72, 114)",
  },
];

const sliderContainer = document.querySelector(".carousel .carousel-container");
let count = 0;
setInterval(function () {
  count++;
  if (count >= sliderData.length) {
    count = 0;
  }
  sliderContainer.innerHTML = "";
  let sliderItem = document.createElement("div");
  sliderItem.className = "carousel-item";

  sliderItemImg = document.createElement("img");
  sliderItemImg.src = sliderData[count].url;
  sliderItemImg.referrerPolicy = "no-referrer";

  sliderItem.appendChild(sliderItemImg);
  sliderContainer.appendChild(sliderItem);

  const sliderFooter = document.querySelector(".carousel .carousel-footer");
  sliderFooter.style.backgroundColor = sliderData[count].color;

  const sliderFooterTitle = document.querySelector(".carousel .carousel-footer .carousel-tool a span");
  sliderFooterTitle.innerText = sliderData[count].title;

  const sliderFooterUl = document.querySelector(".carousel .carousel-footer .carousel-tool ul");
  const sliderFooterLi = sliderFooterUl.querySelectorAll("li");
  sliderFooterLi.forEach((item, index) => {
    if (index === count) {
      item.classList.add("active");
    } else {
      item.classList.remove("active");
    }
  });
}, 2000);

作业

网页时钟(图形版)

网页时钟(图形版) (codepen.io)

  • 需求:网页时钟(图形版)
  • 分析:
    • 创建日期对象,获取当前时间
    • 多次定时器,重复获取时间,让指针动起来
html
<!--
  - 需求:网页时钟(图形版)
  - 分析:
    - 创建日期对象 获取当前时间
    - 多次定时器,重复获取时间,让指针动起来
-->
<div class="clock">
  <div class="hand hour-hand"></div>
  <div class="hand minute-hand"></div>
  <div class="hand second-hand"></div>
</div>
css
.clock {
  width: 600px;
  height: 600px;
  margin: 50px auto;
  position: relative;
  background: url("https://s2.loli.net/2023/04/23/aKd5qAhiJ7jB8Dy.jpg") no-repeat;
}

.hand {
  position: absolute;
  width: 100%;
  height: 100%;
  left: 0;
  top: 0;
}

.hour-hand {
  background: url("https://s2.loli.net/2023/04/23/kKr5EMIBp6RLCjN.png") no-repeat center;
}

.minute-hand {
  background: url("https://s2.loli.net/2023/04/23/6RLfvyu1bG2rseT.png") no-repeat center;
}

.second-hand {
  background: url("https://s2.loli.net/2023/04/23/PBpfJsNwSZbnDFH.png") no-repeat center;
}
js
const hourHand = document.querySelector(".hour-hand");
const minuteHand = document.querySelector(".minute-hand");
const secondHand = document.querySelector(".second-hand");

function setDate() {
  const now = new Date();
  console.log(now);

  const seconds = now.getSeconds();
  const secondsDegrees = (seconds / 60) * 360;
  secondHand.style.transform = `rotate(${secondsDegrees}deg)`;

  const minutes = now.getMinutes();
  const minutesDegrees = (minutes / 60) * 360 + (seconds / 60) * 6;
  minuteHand.style.transform = `rotate(${minutesDegrees}deg)`;

  const hours = now.getHours();
  const hoursDegrees = (hours / 12) * 360 + (minutes / 60) * 30;
  hourHand.style.transform = `rotate(${hoursDegrees}deg)`;
}

setInterval(setDate, 1000);

焦点图案例

焦点图案例 (codepen.io)

  • 需求:QQ 音乐 10 屏轮播图

  • 分析:

    • 获取元素图片 和 h3
    • 创建定时器,修改图片的 src 属性和 h3 文字内容
  • 数据

    js
    const data = [
      {
        imgSrc: "https://s2.loli.net/2023/04/24/IMi9VWmCfLDekPG.webp",
        title: "挑战云歌单,欢迎你来",
      },
      {
        imgSrc: "https://s2.loli.net/2023/04/24/CwdqPzyf924uBTQ.webp",
        title: "田园日记,上演上京记",
      },
      {
        imgSrc: "https://s2.loli.net/2023/04/24/vE4siDBoaylFmPr.webp",
        title: "甜蜜攻势再次回归",
      },
      {
        imgSrc: "https://s2.loli.net/2023/04/24/gIQR7XDynqfv1od.webp",
        title: "我为歌狂,生为歌王",
      },
      {
        imgSrc: "https://s2.loli.net/2023/04/24/b5wN1fdsyvzP27r.webp",
        title: "年度校园主题活动",
      },
      {
        imgSrc: "https://s2.loli.net/2023/04/24/EAjuD8hv9kocUb5.webp",
        title: "pink 老师新歌发布,5 月 10 号正式推出",
      },
      {
        imgSrc: "https://s2.loli.net/2023/04/24/UIp9orDSFkvT87O.webp",
        title: "动力火车来到西安",
      },
      {
        imgSrc: "https://s2.loli.net/2023/04/24/laO39sJqpjLwyoi.webp",
        title: "钢铁侠 3,英雄镇东风",
      },
      {
        imgSrc: "https://s2.loli.net/2023/04/24/CRSn2pu3yvt9gTi.webp",
        title: "我用整颗心来等你",
      },
    ];
html
<!-- QQ 音乐 10 屏轮播图  -->
<div class="qq-music__wrapper">
  <img class="qq-music__pic" src="https://s2.loli.net/2023/04/24/IMi9VWmCfLDekPG.webp" alt="" />
  <div class="qq-music__content">
    <h3 class="qq-music__content__title">挑战云歌单,欢迎你来</h3>
  </div>
</div>
css
.qq-music__wrapper {
  width: 700px;
  height: 320px;
  margin: 50px auto;
  background-color: #000;
  position: relative;
}

.qq-music__content {
  width: 700px;
  height: 53px;
  line-height: 53px;
  position: absolute;
  bottom: 0px;
  background-color: rgba(0, 0, 0, 0.8);
  z-index: 10;
}

.qq-music__content__title {
  width: 82%;
  margin: 0;
  margin-right: 20px;
  padding-left: 20px;
  color: #98e404;
  font-size: 28px;
  float: left;
  font-weight: 500;
  font-family: "Microsoft Yahei", Tahoma, Geneva;
}
js
const data = [
  {
    imgSrc: "https://s2.loli.net/2023/04/24/IMi9VWmCfLDekPG.webp",
    title: "挑战云歌单,欢迎你来",
  },
  {
    imgSrc: "https://s2.loli.net/2023/04/24/CwdqPzyf924uBTQ.webp",
    title: "田园日记,上演上京记",
  },
  {
    imgSrc: "https://s2.loli.net/2023/04/24/vE4siDBoaylFmPr.webp",
    title: "甜蜜攻势再次回归",
  },
  {
    imgSrc: "https://s2.loli.net/2023/04/24/gIQR7XDynqfv1od.webp",
    title: "我为歌狂,生为歌王",
  },
  {
    imgSrc: "https://s2.loli.net/2023/04/24/b5wN1fdsyvzP27r.webp",
    title: "年度校园主题活动",
  },
  {
    imgSrc: "https://s2.loli.net/2023/04/24/EAjuD8hv9kocUb5.webp",
    title: "pink 老师新歌发布,5 月 10 号正式推出",
  },
  {
    imgSrc: "https://s2.loli.net/2023/04/24/UIp9orDSFkvT87O.webp",
    title: "动力火车来到西安",
  },
  {
    imgSrc: "https://s2.loli.net/2023/04/24/laO39sJqpjLwyoi.webp",
    title: "钢铁侠 3,英雄镇东风",
  },
  {
    imgSrc: "https://s2.loli.net/2023/04/24/CRSn2pu3yvt9gTi.webp",
    title: "我用整颗心来等你",
  },
];

const pic = document.querySelector(".qq-music__pic");
const title = document.querySelector(".qq-music__content__title");

let index = 0;
setInterval(function () {
  index++;
  if (index >= data.length) {
    index = 0;
  }
  pic.src = data[index].imgSrc;
  title.innerHTML = data[index].title;
}, 1000);